package com.szwistar.common.comm.blocktrans;

import org.apache.mina.core.buffer.IoBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 块传输协议帧
 */
public class BlockTransFrame {
	private static Logger LOG = LoggerFactory.getLogger("Default");

	// 同步头标志(6字节)
	public static byte[] SYNC = { (byte)0xF0, (byte)0xE1, (byte)0xD2, (byte)0xA3, (byte)0xB4, (byte)0xC5 };
	// 最小帧长度(不含前导码长度)
	public static final int MIN_FRAME_LEN = 10;
	// 最大帧长度(不含前导码长度)
	public static final int MAX_FRAME_LEN = 8*1024*1024;
	// 开销长度(len+type+reserve+checksum)
	public static final int COST_LEN = 12;

	// 数据长度(4字节，不包括 SYNC 和 自己)
	protected int len;
	// 帧子类型标识(2字节)
	protected short type;
	// (保留2字节)
	protected short reserve;

	// 净荷 ...
	protected BlockTransPayload payload;

	// 校验码(4字节，包括len .. body，在帧的最后)
	protected int checksum;

	/**
	 * 净荷解码
	 * @param typeId
	 * @param in
	 * @param length
	 * @return
	 * @throws Exception
	 */
	public static BlockTransPayload decodePayload(short typeId, IoBuffer in, int length) {
		Class<? extends BlockTransPayload> typeClazz = BlockTransFrameCodec.getPayloadTypeById(typeId);

		if(length < 1) {
			LOG.error("净荷为空，无法解码：{}");
			return null;
		}

		if(typeClazz == null) {
			LOG.error("净荷解码失败，未注册的净荷类型：{}", typeId);
			return null;
		}

		try {
			// 创建对应的净荷类实例
			BlockTransPayload payload = typeClazz.newInstance();
			// 解码
			payload.decode(in, length);
			return payload;
		} catch (Exception e) {
			LOG.error("净荷解码失败！", e);
		}

		return null;
	}

	/**
	 * 根据帧类型，调用对应的派生类解码函数进行解码，生成正确的对象
	 */
	public static BlockTransFrame createByDecode(IoBuffer in) throws Exception {
		return new BlockTransFrame().decode(in);
	}

	protected BlockTransFrame() {
	}

	public BlockTransFrame(BlockTransPayload payload) {
		this.payload = payload;
	}

	/**
	 * 解码帧数据
	 * @return
	 * @throws Exception
	 */
	public BlockTransFrame decode(IoBuffer in) throws Exception {
		len = in.getInt();
		type = in.getShort();
		reserve = in.getShort();
		payload = decodePayload(type, in, (4 + len - COST_LEN));
		checksum = in.getInt();
		return this;
	}

	/**
	 * 分配分配缓冲区，把帧编码并放入缓冲区
	 * @param out
	 * @return
	 */
	public IoBuffer encode() {
		IoBuffer out = null;
		try {
			// ！！！注意：按最大帧长度分配缓冲区，因为帧长度不可预测
			out = IoBuffer.allocate(MAX_FRAME_LEN, true);

			// 填写同步头
			out.put(SYNC);

			// 因为帧长度不确定，帧长度先填写为 0，占位
			out.putInt(0);

			// 填写帧类型
			out.putShort(payload.typeId());
			// 保留字段
			out.putShort(reserve);

			// 填写净荷编码
			payload.encode(out);

			// 重新填写帧长度
			len = out.position();
			out.position(SYNC.length);
			out.putInt(len - SYNC.length);
			// 恢复偏移地址
			out.position(len);

			// 计算帧校验和
			out.putInt(calcChecksum());
		} catch (Exception e) {
			// ！！！注意：如果出现异常，一定要释放缓冲区！
			if(out != null) {
				out.free(); out = null;
			}
			LOG.error("编码失败！", e);
		}
		return out;
	}

	/**
	 * (解码时)检查校验值
	 */
	protected boolean checksum() {
		return false;
	}

	/**
	 * (编码时)计算校验值
	 */
	protected int calcChecksum() {
		return checksum;
	}

	@Override
	public String toString() {
		String strPayload = (payload == null)?"(empty)":payload.toString();
		return String.format("len=%d, type=%d, payload={%s}, checksum=%d",
				4+len, type, strPayload, checksum);
	}
}
